﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;

using zijian666.AnyExtensions.Privates;

using static zijian666.AnyExtensions.ExceptionExtensions;

namespace zijian666.AnyExtensions;

/// <summary>
/// 异常扩展
/// </summary>
public static class ValidationExceptionExtensions
{
    /// <summary>
    /// 实体类校验
    /// </summary>
    /// <exception cref="ArgumentException"></exception>
    public static ParameterChecker<T> ValidatEntity<T>(this ParameterChecker<T> checker, bool ignoreNullValue)
    {
        var settings = new ValidationSettings { IgnoreNullValue = ignoreNullValue };
        return ValidatEntity(checker, settings);
    }
    /// <summary>
    /// 实体类校验
    /// </summary>
    /// <exception cref="ArgumentException"></exception>
    public static ParameterChecker<T> ValidatEntity<T>(this ParameterChecker<T> checker, ValidationSettings settings = default)
    {
        var entity = checker.Value ?? throw new ArgumentNullException(checker.Name);
        var ex = ValidatEntityCore(checker.Name, checker.Value, settings, null, []).FirstOrDefault();
        if (ex is null)
        {
            return checker;
        }

        throw new ArgumentException($"调用{checker.Method}参数错误：{ex.Message}", checker.Name, ex);
    }

    private static string DisplayText(this Type type, ValidationSettings settings)
    {
        if (type is null)
        {
            return settings.UseAttributeText ? "<空>" : "<null>";
        }
        if (!settings.UseAttributeText)
        {
            return type.FullName ?? type.ToString();
        }
        return type.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName
            ?? type.GetCustomAttribute<DescriptionAttribute>()?.Description
            ?? type.FullName
            ?? type.ToString();
    }

    private static string DisplayText(this MemberInfo member, ValidationSettings settings)
    {
        if (member is null)
        {
            return settings.UseAttributeText ? "<未知>" : "<unknown>";
        }
        if (!settings.UseAttributeText)
        {
            return member.Name ?? member.ToString();
        }
        return member.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName
            ?? member.GetCustomAttribute<DescriptionAttribute>()?.Description
            ?? member.Name
            ?? member.ToString();
    }

    /// <summary>
    /// 实体类校验
    /// </summary>
    public static IEnumerable<ValidationException> ValidatEntity<T>(this T entity, bool ignoreNullValue)
        => ValidatEntity(entity, new ValidationSettings { IgnoreNullValue = ignoreNullValue });
    /// <summary>
    /// 实体类校验
    /// </summary>
    public static IEnumerable<ValidationException> ValidatEntity<T>(this T entity, string entityName, bool ignoreNullValue)
        => ValidatEntity(entity, entityName, new ValidationSettings { IgnoreNullValue = ignoreNullValue });

    /// <summary>
    /// 实体类校验
    /// </summary>
    public static IEnumerable<ValidationException> ValidatEntity<T>(this T entity, ValidationSettings settings = default)
        => ValidatEntityCore(typeof(T).DisplayText(settings), entity, settings, null, []);
    /// <summary>
    /// 实体类校验
    /// </summary>
    public static IEnumerable<ValidationException> ValidatEntity<T>(this T entity, string entityName, ValidationSettings settings = default)
        => ValidatEntityCore(entityName, entity, settings, null, []);

    #region private

    private static IEnumerable<ValidationException> ValidatEntityCore(string entityName, object? entity, ValidationSettings settings, string? prefix, HashSet<object?> context)
    {
        if (entity is null)
        {
            throw new ArgumentNullException(nameof(entity));
        }
        if (!context.Add(entity))
        {
            yield break;
        }

        if (entity is IEnumerable array and not string)
        {
            if (entity is IDictionary dict)
            {
                foreach (DictionaryEntry item in dict)
                {
                    foreach (var error in ValidatEntityCore(entityName, item.Value, settings, prefix + item.Key + ".", context))
                    {
                        yield return error;
                    }
                }
            }
            else
            {
                int i = 0;
                foreach (var item in array)
                {
                    foreach (var error in ValidatEntityCore(entityName, item, settings, prefix + i + ".", context))
                    {
                        yield return error;
                    }
                    i++;
                }
            }
        }


        foreach (var reader in PropertyReader.GetInstance(entity.GetType()))
        {
            if (settings.Filter?.Invoke(reader.Property) is false)
            {
                continue;
            }
            var value = reader.GetValue(entity);
            if (value is DBNull)
            {
                value = null;
            }
            if (value is null && settings.IgnoreNullValue)
            {
                continue;
            }
            foreach (var attribute in reader.Validators)
            {
                if (!settings.RequireValidation(attribute))
                {
                    continue;
                }
                if (!attribute.IsValid(value))
                {
                    var propertyName = prefix + reader.Property.DisplayText(settings);
                    var message = settings.PrefixSource
                        ? $"[{entityName}].[{propertyName}]={value.GetInfoString()}; 验证失败: {attribute.FormatErrorMessage(propertyName)}"
                        : attribute.FormatErrorMessage(propertyName);
                    yield return new ValidationException(new ValidationResult(message, [propertyName]), attribute, value)
                    {
                        Data =
                        {
                            ["PropertyInfo"] = reader.Property
                        }
                    };
                }
            }
            if (value is not null)
            {
                foreach (var error in ValidatEntityCore(entityName, value, settings, prefix + reader.Property.Name + ".", context))
                {
                    yield return error;
                }
            }
        }
    }

    /// <summary>
    /// 获取当前指定对象的字符串
    /// </summary>
    private static string GetInfoString(this object? value, int maxLength = 100)
    {
        if (value is null)
        {
            return "null";
        }
        if (value is DBNull)
        {
            return "DBNull";
        }
        try
        {
            if (maxLength < 15)
            {
                maxLength = 15;
            }
            var text = value.ToString() ?? "";
            if (text.Length > maxLength)
            {
                var postfix = "...(总长度:" + text.Length + ")";
                text = text.Substring(0, maxLength - postfix.Length) + postfix;
            }
            return text;
        }
        catch (Exception)
        {
            return value?.GetType().ToString() ?? "[未知]";
        }
    }

    #endregion
}
